package org.lds.scheduled.job.admin.starter.health;

import org.apache.commons.collections4.CollectionUtils;
import org.lds.scheduled.job.common.constant.CommonConstant;
import org.lds.scheduled.job.common.model.dto.ResultDTO;
import org.lds.scheduled.job.common.model.db.PageResult;
import org.lds.scheduled.job.common.util.RestUtil;
import org.lds.scheduled.job.common.util.ScheduledUtil;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.ParameterizedTypeReference;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

public abstract class AbstractInstanceHealthCheckStarter<T> implements ApplicationRunner {
    /**
     * key: id
     * value: 不健康的次数 默认为2
     */
    private Map<Long, Integer> noHealthIdCountMap = new HashMap<>();

    private Long id = 0L;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        ScheduledUtil.defaultScheduled(() -> execute());
    }

    private void execute() {
        // 分页查询500条
        ResultDTO<PageResult<T>> resultDTO = pageInstances();
        if (!ResultDTO.isSuccess(resultDTO) || Objects.isNull(resultDTO.getData())) return;

        List<T> instances = resultDTO.getData().getData();

        // 如果数据为空 重置查询
        if (CollectionUtils.isEmpty(instances)) {
            id = 0L;
            return;
        }

        // 已经是最后一页
        if (instances.size() < 500) {
            id = 0L;
        } else {
            id = id(instances.get(instances.size() - 1));
        }

        // 检查ip:port是否健康
        instances.forEach(instance -> {
            boolean health = health(instance);
            if (health) {
                // 更新状态为已上线
                if(!enable(instance)) {
                    updateInstance(id(instance), true);
                }
            } else {
                // 更新状态为已下线
                if(enable(instance)) {
                    offline(id(instance));
                }
            }
        });
    }

    private boolean health(T instance) {
        try {
            ResultDTO resultDTO = RestUtil.post(address(instance) + CommonConstant.HEALTH, null, new ParameterizedTypeReference<ResultDTO>() {});
            return ResultDTO.isSuccess(resultDTO);
        } catch (Exception e) {
            return false;
        }
    }

    protected abstract ResultDTO<PageResult<T>> pageInstances();
    protected abstract Long id(T instance);
    protected abstract String address(T instance);
    protected abstract boolean enable(T instance);
    protected abstract void updateInstance(Long id, boolean enable);

    /**
     * 下线(尽量屏蔽网络波动的影响)
     * @param id
     */
    private void offline(Long id) {
        Integer count = noHealthIdCountMap.getOrDefault(id, 0) + 1;
        if (count >= 2) {
            noHealthIdCountMap.remove(id);

            updateInstance(id, false);
        } else {
            noHealthIdCountMap.put(id, count);
        }
    }

    public Long getId() {
        return id;
    }
}
